﻿using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

[Serializable]
public class AnimationData
{
    public float length;
    public float m_fDurationTime;
    public string m_sAnimationName;
    public float m_fFrameRate;
    public bool m_bIsLoop;

    public List<CurveBinding> curveBindings;
    public AnimationData()
    {
        curveBindings = new List<CurveBinding>();
    }
    public int GetAllFrameNumber()
    {
        return HelpTool.TimeToFrame(length, m_fFrameRate);
    }

    public void LerpBinding()
    {
        if (IsValideKeyFarme())
        {
            int number = GetAllFrameNumber();

            for (int i = 0; i < curveBindings.Count; i++)
            {
                curveBindings[i].EvaluateKeyFrame(number, m_fFrameRate);
            }
        }
    }

    public bool IsValideKeyFarme()
    {
        if (curveBindings.Count <= 0)
            return false;

        foreach (var item in curveBindings)
        {
            if (item.m_KeyFrames.Count <= 0)
                return false;
        }
        return true;
    }

    public bool IsValidValue()
    {
        if (curveBindings.Count <= 0)
            return false;
        foreach (var item in curveBindings)
        {
            if (item.finalKeyFrames == null)
                return false;
            if (item.finalKeyFrames.Count <= 0)
                return false;
        }
        return true;
    }

    public void WriteToJson(string name)
    {
        string json = JsonUtility.ToJson(this, true);

        if (!Directory.Exists(AnimConfig.folderPath + "/Temp"))
            Directory.CreateDirectory(AnimConfig.folderPath + "/Temp");

        string path = AnimConfig.folderPath + "/Temp/" + name + ".json";
        File.WriteAllText(path, json);
    }
}

[Serializable]
public class CurveBinding
{
    public float averageDuration;
    public string path;
    public string propertyName;
    public List<m_KeyFrame> m_KeyFrames = new List<m_KeyFrame>();//曲线上k的所有关键帧
    public List<m_KeyFrame> finalKeyFrames;                      //插值所有帧数后的集合

    public void EvaluateKeyFrame(int count, float frameRate)
    {
        AnimationCurve curve = new AnimationCurve();
        foreach (var item in m_KeyFrames)
        {
            Keyframe keyframe = new Keyframe(item.time, item.value);
            keyframe.inTangent = item.inTangent;
            keyframe.outTangent = item.outTangent;
            keyframe.inWeight = item.inWeight;
            keyframe.outWeight = item.outWeight;
            keyframe.weightedMode = item.weightedMode;
            curve.AddKey(keyframe);
        }

        finalKeyFrames = new List<m_KeyFrame>();

        for (int i = 0; i < count + 1; i++)
        {
            float curTime = HelpTool.FrameToTime(i, frameRate);
            m_KeyFrame newK = GetFrame(curTime);

            if (newK == null)
            {
                newK = new m_KeyFrame();
                newK.time = curTime;
                newK.value = curve.Evaluate(curTime);
            }
            finalKeyFrames.Add(newK);
        }
    }


    //自己计算的方式，不知道untiy里如何插值，弃用
    public void LerpKeyFrame(int count, float frameRate)
    {
        finalKeyFrames = new List<m_KeyFrame>();

        for (int i = 0; i < count + 1; i++)
        {
            float curTime = HelpTool.FrameToTime(i, frameRate);
            m_KeyFrame newK = GetFrame(curTime);

            if (newK == null)
            {
                newK = new m_KeyFrame();
                newK.time = curTime;
                m_KeyFrame last = GetLastPosValue(newK.time);
                m_KeyFrame next = GetNextPosValue(newK.time);
                if (last == null || next == null)
                {
                    Debug.LogError("动画曲线k帧存在特殊情况，需要处理：" + path + " -- " + propertyName);
                    newK.value = 0;
                }
                else
                {
                    float value = GetBetweenKeyFrame(last, next, newK.time);
                    newK.value = value;
                }
            }
            finalKeyFrames.Add(newK);
        }
    }

    float GetBetweenKeyFrame(m_KeyFrame k1, m_KeyFrame k2, float time)
    {
        float p0 = k1.value;
        float p1 = k2.value;
        float dx = k2.time - k1.time;

        float m0 = k1.outTangent;
        float m1 = k2.inTangent;
        float cubicTime = time * time * time;
        float squareTime = time * time;
        float h00 = 2 * cubicTime - 3 * squareTime + 1;
        float h10 = cubicTime - 2 * squareTime + time;
        var h01 = -2 * cubicTime + 3 * squareTime;
        var h11 = cubicTime - squareTime;

        return h00 * p0 + h10 * m0 * dx + h01 * p1 + h11 * dx * m1;
    }

    public m_KeyFrame GetFrame(float time)
    {
        foreach (var item in m_KeyFrames)
        {
            if (Mathf.Approximately(item.time, time))
            {
                return item;
            }
        }
        return null;
    }

    m_KeyFrame GetLastPosValue(float time)
    {
        List<m_KeyFrame> temp = new List<m_KeyFrame>();

        for (int i = 0; i < m_KeyFrames.Count; i++)
        {
            if (m_KeyFrames[i].time < time)
            {
                temp.Add(m_KeyFrames[i]);
            }
        }
        if (temp.Count > 0)
        {
            temp.Sort((a, b) => { return a.time.CompareTo(b.time); });
            return temp[temp.Count - 1];
        }

        return null;
    }

    m_KeyFrame GetNextPosValue(float time)
    {
        List<m_KeyFrame> temp = new List<m_KeyFrame>();

        for (int i = 0; i < m_KeyFrames.Count; i++)
        {
            if (m_KeyFrames[i].time > time)
            {
                temp.Add(m_KeyFrames[i]);
            }
        }
        if (temp.Count > 0)
        {
            temp.Sort((a, b) => { return a.time.CompareTo(b.time); });
            return temp[0];
        }
        return null;
    }
}

[Serializable]
public class m_KeyFrame
{
    public float time;
    public float value;
    public float inTangent;
    public float outTangent;
    public float inWeight;
    public float outWeight;
    public WeightedMode weightedMode;
}
